package speed2010.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.semanticweb.owl.model.OWLOntologyCreationException;

import speed2010.matcher.semanticmatching.SemanticMatcher;
import speed2010.middleware.comunicacao.Address;
import speed2010.ontologies.ClusterOntology;
import speed2010.ontologies.LocalOntology;
import speed2010.ontologies.Ontology;
import speed2010.ontologies.OntologyConcept;
import speed2010.ontologies.OntologyParser;
import speed2010.ontologies.SummarizedOntology;
import JavaMicroBenchmark.JavaMicroBenchmark;
import JavaMicroBenchmark.JavaMicroBenchmarkCreateFile;
import JavaMicroBenchmark.JavaMicroBenchmarkResult;
import speed2010.matcher.exceptions.InvalidOntologyFileException;
import speed2010.matcher.semanticmatching.basics.Alignment;
import speed2010.matcher.semanticmatching.basics.ESSMFunction;
import speed2010.matcher.semanticmatching.basics.SSMResult;
import speed2010.matcher.sparql.StorageManager;
import speed2010.merger.SemMerging;
import speed2010.merger.Unmerge;

/**
 * Classe Wrapper, ou seja, irá uniformizar TODAS as chamadas dos componentes.
 * 
 */
public class Wrapper implements Serializable {

	private static final long serialVersionUID = 6882964965736663944L;

	static Wrapper instance = null;

	public Wrapper() {

	}

	static public Wrapper getInstance() {
		if (instance == null) {
			instance = new Wrapper();
		}
		return instance;
	}

	/**
	 * Recebe como parâmetro o caminho absoluto das ontologias (salvas em
	 * disco), e sobrescreve a Cluster Ontology adicionando todos os conceitos
	 * presentes na Local Ontology.
	 * 
	 * @param caminhoClusterOntology
	 * @param caminhoLocalOntology
	 * @throws Exception
	 */
	synchronized public void merge(SpeedClusterOntology clusterOntology,
			SpeedLocalOntology localOntology) throws Exception {

		String caminhoClusterOntology = clusterOntology.ontology.getLocation();
		String caminhoLocalOntology = localOntology.ontology.getLocation();
//		System.out.println("Caminhos:\n" + caminhoClusterOntology + "\n"
//				+ caminhoLocalOntology);
		File file = new File("domainOntology.owl");
		String caminhoDomainOntology = file.getAbsolutePath().replaceAll(
				"\\\\", "/");
		//String caminhoResultado = "C:/UFPE/SPEED/Workspace/SPEED_Integrado/ResultadoMerge.owl";
		SemMerging temp = new SemMerging(caminhoDomainOntology, caminhoClusterOntology, 
				caminhoLocalOntology, caminhoClusterOntology, clusterOntology);
		temp.start();
		
		clusterOntology.updateOntology();
		/*
		ArrayList<OntologyConcept> conceitos = clusterOntology.ontology.getConcepts();
		
		for (int i = 0; i < conceitos.size(); i++){
			
			OntologyConcept conceito = conceitos.get(i);
			System.out.println("Conceito: " + conceito.getName() + " Frequencia: " + conceito.getFrequency());
		}
		*/
		
	}

	/**
	 * Matcher utilizado pelo Peer Semântico, para comparar a LocalOntology de
	 * um peer requisitante com os sumários armazenados dos clusters.
	 * 
	 * @param ontology
	 * @param summary
	 * @return
	 */
	synchronized public int matcher(SpeedLocalOntology ontology,
			SpeedSummarizedOntology summary) {
		Iterator<OntologyConcept> ontologyConcepts = ontology.ontology.getConcepts().iterator();
		Iterator<OntologyConcept> summaryConcepts = summary.ontology.getConcepts().iterator();
		// System.out.println("-----Tamanhos:" +
		// ontology.ontology.getConcepts().size() + " e " +
		// summary.ontology.getConcepts().size());
		int numberOfSameConcepts = 0;
		while (ontologyConcepts.hasNext()) {
			OntologyConcept ontologyConcept = ontologyConcepts.next();

			while (summaryConcepts.hasNext()) {
				OntologyConcept summaryConcept = summaryConcepts.next();

				if (ontologyConcept.equals(summaryConcept)) {
					numberOfSameConcepts++;
				}
			}
			summaryConcepts = summary.ontology.getConcepts().iterator();
		}

		return numberOfSameConcepts;
	}

	/**
	 * Matcher utilizado pelo Peer de Integração para comparar a proximidade com
	 * uma LocalOntology de um Peer Requisitante, ou uma ClusterOntology de um
	 * Peer de Integração vizinho.
	 * 
	 * @param clusterOntology
	 * @param ontology
	 *            (pode ser LocalOntology ou ClusterOntology)
	 * @return
	 * @throws IOException
	 */
	synchronized public double matcher(SpeedClusterOntology clusterOntology,
			SpeedLocalOntology ontology) throws IOException {

		String CLO = clusterOntology.ontology.getLocation();
		String LO = ontology.ontology.getLocation();
		// Atencao! as classes de ontologias e Matcher nao foram feitas
		// considerando a implementação
		// distribuida, o que irá gerar erro neste código, pois a propriedade
		// "location" é definida
		// na criação da ontologia pelo peer original, quando repassada na rede
		// irá representar um
		// path inválido. Ou seja, é necessário salvar a ontologia localmente e
		// atualizar o location

		// é o caminho onde a ontologia de domínio se encontra em disco
		File file = new File("domainOntology.owl");
		String communityOntologyPath = file.getAbsolutePath().replaceAll(
				"\\\\", "/");
		double resultadoSSM = 0;
		try {

//			System.out.println(LO);
//			System.out.println(CLO);
//			System.out.println("-");
			//System.out.println(communityOntologyPath);
			
			SemanticMatcher sm = new SemanticMatcher(LO, CLO,
					communityOntologyPath);
//			File f1 = new File(LO);
//			File f2 = new File(CLO);
//			File f3 = new File(communityOntologyPath);
//			File f4 = new File("finalAlignment.rdf");
//			System.out.println(LO + " write = " + f1.canWrite() + " read = " + f1.canRead());
//			System.out.println(CLO+ " write = " + f2.canWrite() + " read = " + f2.canRead());
//			System.out.println(communityOntologyPath+ " write = " + f3.canWrite() + " read = " + f3.canRead());
//			System.out.println(f4.getAbsolutePath() + " write = " + f4.canWrite() + " read = " + f4.canRead());
			if (sm != null) {
				// OntologyMatcherProperties.FILE_FOLDER = "/";
//				System.err.println("\n\n/n/nAntes");
				SSMResult result = sm.calculateSSM(ESSMFunction.Average,
						"finalAlignment.rdf");
//				System.err.println("Depois - cluster:"
//					+ clusterOntology.ontology.getLocation() + " e \nlocal:" + ontology.ontology.getLocation());
				if (result != null) {
					resultadoSSM = result.getSsm();
				} else {
					new Exception("Result NULL");
				}
			} else {
				new Exception("sm NULL");
			}
		} catch (Exception e) {
			System.err.println("Erro no matcher. FERROUUUUUUU. \ncluster:"
					+ clusterOntology.ontology.getLocation() + " e \nlocal:" + ontology.ontology.getLocation());
			e.printStackTrace();	
			if(resultadoSSM == 0){
				resultadoSSM = 0.5;
				System.err.println("Definindo o resultadoSSM = 0.5 por conta da exceção");
			}
		}
		return resultadoSSM;
	}
	
	
	synchronized public double matcher(SpeedClusterOntology clusterOntology1,
			SpeedClusterOntology clusterOntology2) throws IOException {

		String CLO1 = clusterOntology1.ontology.getLocation();
		String CLO2 = clusterOntology2.ontology.getLocation();
		// Atencao! as classes de ontologias e Matcher nao foram feitas
		// considerando a implementação
		// distribuida, o que irá gerar erro neste código, pois a propriedade
		// "location" é definida
		// na criação da ontologia pelo peer original, quando repassada na rede
		// irá representar um
		// path inválido. Ou seja, é necessário salvar a ontologia localmente e
		// atualizar o location

		// é o caminho onde a ontologia de domínio se encontra em disco
		File file = new File("domainOntology.owl");
		String communityOntologyPath = file.getAbsolutePath().replaceAll(
				"\\\\", "/");
		double resultadoSSM = 0;
		try {
		
			SemanticMatcher sm = new SemanticMatcher(CLO2, CLO1,
					communityOntologyPath);

			if (sm != null) {

				SSMResult result = sm.calculateSSM(ESSMFunction.Average,
						"finalAlignment.rdf");

				if (result != null) {
					resultadoSSM = result.getSsm();
				} else {
					new Exception("Result NULL");
				}
			} else {
				new Exception("sm NULL");
			}
		} catch (Exception e) {
			System.err.println("Erro no matcher. FERROUUUUUUU. \nclusterOntology1:"
					+ clusterOntology1.ontology.getLocation() + " e \nclusterOntology2:" + clusterOntology2.ontology.getLocation());
			e.printStackTrace();	
			if(resultadoSSM == 0){
				resultadoSSM = 0.5;
				System.err.println("Definindo o resultadoSSM = 0.5 por conta da exceção");
			}
		}
		return resultadoSSM;
	}
	

	/**
	 * Sumarizador, recebe uma clusterOntology e atualiza os seus conceitos
	 * internamente, para então gerar e retornar o sumário a ser encaminhado
	 * para o Peer Semântico.
	 * 
	 * @param clusterOntology
	 * @return
	 * @throws Exception
	 */
	synchronized public SpeedSummarizedOntology summarize(
			SpeedClusterOntology ontologiaCluster, Address endereco) {

		ClusterOntology clusterOntology = ontologiaCluster.ontology;
		// FIXME o construtor abaixo utiliza o "IP" EXCLUSIVAMENTE para
		// comparação
		// de igualdade de sumários, e ainda por cima assume que o IP é um int.
		// Será preciso converter os IPs em ints, e passá-lo como parâmetro do
		// método, para
		// que o Peer Semântico possa corretamente substituir os sumários
		// atualizados recebidos.
		// Outra alternativa é utilizarmos um construtor próprio, que envolva um
		// summary.
		SummarizedOntology summary = new SummarizedOntology(/* this.getIp() */0);

		OntologyParser parser = new OntologyParser(clusterOntology);
		try {
			parser.start();
			summary.summarize(clusterOntology);
		} catch (OWLOntologyCreationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}// esta chamada altera internamente as propriedades da clusterOntology,
		// ou seja, assume-se que sempre será gerado o sumário após a cluster
		// ontology ser criada
		// ou modificada, para que suas informações internas fiquem
		// consistentes.

		return new SpeedSummarizedOntology(summary, endereco);
	}

	/**
	 * Método utilizado por um Peer Entrante quando cria um cluster próprio.
	 * Transforma a LocalOntology em uma ClusterOntology, assumindo que a
	 * primeira já está salva em disco e com o atributo "location" correto.
	 * 
	 * @param ontology
	 * @return
	 * @throws Exception
	 */
	synchronized public SpeedClusterOntology createClusterOntology(
			SpeedLocalOntology ontology) throws Exception {
		//System.out.println("createClusterOntology: " + ontology.location);
		String newFileName = generateFileName(ontology.ontology);

		File file = new File(newFileName);
		if (file.exists()) {
			file.delete();
		}
		file = new File(newFileName);
//		System.out.println("****Novo nome: "
//				+ file.getAbsolutePath().replaceAll("\\\\", "/"));
		file.createNewFile();
		copyFile(ontology.ontology, file);
		// FIXME rever o que seria colocado no terceiro argumento
		return new SpeedClusterOntology(new ClusterOntology(ontology.ontology
				.getId(), file.getAbsolutePath().replaceAll("\\\\", "/"),
				"education"));
	}

	/**
	 * Método auxiliar para a criação de uma ClusterOntology
	 * 
	 * @param ontology
	 * @return
	 */
	synchronized private String generateFileName(LocalOntology ontology) {
		String fileLocation = ontology.getLocation();
		int index = fileLocation.lastIndexOf('/');
		int length = fileLocation.length();

		String fileName = fileLocation.substring(index + 1, length);
		String RegEx = "LO";
		String REPLACE = "CLO";
		Pattern p = Pattern.compile(RegEx);
		Matcher m = p.matcher(fileName);
		fileName = m.replaceAll(REPLACE);

		String filePath = fileLocation.substring(0, index + 1);

		String newFileName = filePath + fileName;
		return newFileName;
	}

	/**
	 * Método auxiliar para a criação de uma ontologia
	 * 
	 * @param ontology
	 * @param file
	 * @throws Exception
	 */
	synchronized private void copyFile(Ontology ontology, File file)
			throws Exception {
		PrintWriter printer = new PrintWriter(new FileWriter(file));
		BufferedReader reader = new BufferedReader(new FileReader(new File(
				ontology.getLocation())));

		String line = reader.readLine();
		while (line != null) {
			printer.println(line);
			line = reader.readLine();
		}

		reader.close();
		printer.close();
	}

	// TODO: Rever esse método e fazê-lo funcionar
	synchronized public SummarizedOntology matcherInicializarSumarioOntologiaCluster(
			LocalOntology ontologiaLocal) {
		return new SummarizedOntology(0);
	}

	/**
	 * Método que salva o resultado do benchmark em um arquivo e depois extrai
	 * esse resultado desse arquivo.
	 * 
	 * @param diretorioTemporario
	 * @return resultado do benchmark
	 */
	synchronized public Long benchmark(String diretorioTemporario) {
		diretorioTemporario = diretorioTemporario.replace(':', '_');
		diretorioTemporario = diretorioTemporario.replace(' ', '_');
		JavaMicroBenchmark jmb = new JavaMicroBenchmark();
		String[] args = null;
		 //TODO: Comentado para agilizar a implementação.
		 long resultado = -1L;
		// TODO: Comentar linha abaixo quando descomentar linha de cima.
/////		long resultado = Math.round(Math.random()*14233) % 1000;
		PrintStream ps;
		// TODO: Comentado para agilizar a implementação.
		
		  try { PrintStream old = System.out; //Guardando o default do System
		  String url = null; String nomeDoArquivoTemp =
		  "resultadoBenchmark.txt"; if (diretorioTemporario != null) { File dir
		  = new File(diretorioTemporario); if (!dir.isDirectory() &&
		  !dir.exists()) { dir.mkdir(); } url = diretorioTemporario + "\\" +
		  nomeDoArquivoTemp; } else { url = nomeDoArquivoTemp; }
		  
		  ps = new PrintStream(url); System.setOut(ps); jmb.main(args);
		  //Rodando o benchmark System.setOut(old); 
		  FileReader reader = new FileReader(url); //DEVE EXISTIR UM ARQUIVO TXT DE BENCHMARK, SENÂO VAI DAR ERRO DE EXECUÇÃO!!! 
		  BufferedReader leitor = new
		  BufferedReader(reader); StringTokenizer st = null;
		  
		  String linha = null; 
		  int count = 1; 
		  while ((linha = leitor.readLine()) != null) {// Lê o arquivo linha a linha 
			  st = new StringTokenizer(linha); 
			  int tokenNumber = 1; 
			  while (st.hasMoreTokens()) { 
			  String dados = st.nextToken(); //
		  //System.out.println("Linha " + count + " , token " // + tokenNumber +  ": " + dados); 
			  if(count == 17 && tokenNumber == 5){ //Posição do resultado no arquivo //System.out.println(dados); 
				  resultado =   Long.parseLong(Math.round(Double.parseDouble(dados))+"");
				  break; 
			  }
		 
		  tokenNumber++; } count++; }
		  
		  } 
		  catch (FileNotFoundException e) { 
			  e.printStackTrace(); 
			 } catch  (IOException e) {
				 e.printStackTrace(); 
				 }
		 return resultado;
	}
	
	public synchronized void unMerge(SpeedClusterOntology clusterOntology,
			SpeedLocalOntology localOntology){
		
		File file = new File("domainOntology.owl");
		String caminhoDomainOntology = file.getAbsolutePath().replaceAll("\\\\", "/");
		
		Unmerge unmerge = new Unmerge (clusterOntology, 
					localOntology, caminhoDomainOntology);
		
		try {
			unmerge.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		clusterOntology.updateOntology();

	}
	
	/**
	 * Metodo do matcher para gerar o arquivo de alinhamentos semantica (ainda precisa ser atualizado)
	 * 
	 * @param clusterOntology
	 * @param clusterOntology2
	 * @param arquivoAlinhamentos
	 * @return
	 */
	public String gerarAlinhamentos(SpeedClusterOntology clusterOntology, SpeedClusterOntology clusterOntology2,
			String arquivoAlinhamentos) {
		String CLO = clusterOntology.ontology.getLocation();
		String LO = clusterOntology2.ontology.getLocation();

		File file = new File("domainOntology.owl");
		String communityOntologyPath = file.getAbsolutePath().replaceAll("\\\\", "/");
		SemanticMatcher sm;
		String outputPath = null;

		try {
			/*
			 * Checagem do SO, pois classes do matcher n„o levaram em conta que o sistema executaria
			 * em outros SO's e n„o faz o tratamento adequado de path
			 */
			if (!Util.isWindows()) {
				LO = "/" + LO;
				CLO = "/" + CLO;
				communityOntologyPath = "/" + communityOntologyPath;
			}

			sm = new SemanticMatcher(LO, CLO, communityOntologyPath);
			ArrayList<Alignment> aligns = sm.executeSemanticMatchingNormalized();
			//TODO ajeitar o caminho do arquivo que eh criado  PI/vizinho1.rdf
			outputPath = arquivoAlinhamentos + "Alinhamento.owl";
			StorageManager.saveSemanticAlignmentsToRDF(aligns, outputPath);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return outputPath;
	}
	
	public String gerarAlinhamentos(SpeedLocalOntology clusterOntology, SpeedLocalOntology clusterOntology2,
			String arquivoAlinhamentos) {
		String CLO = clusterOntology.ontology.getLocation();
		String LO = clusterOntology2.ontology.getLocation();

		File file = new File("domainOntology.owl");
		String communityOntologyPath = file.getAbsolutePath().replaceAll("\\\\", "/");
		SemanticMatcher sm;
		String outputPath = null;

		try {
			/*
			 * Checagem do SO, pois classes do matcher n„o levaram em conta que o sistema executaria
			 * em outros SO's e n„o faz o tratamento adequado de path
			 */
			if (!Util.isWindows()) {
				LO = "/" + LO;
				CLO = "/" + CLO;
				communityOntologyPath = "/" + communityOntologyPath;
			}

			sm = new SemanticMatcher(LO, CLO, communityOntologyPath);
			ArrayList<Alignment> aligns = sm.executeSemanticMatchingNormalized();
			//TODO ajeitar o caminho do arquivo que eh criado  PI/vizinho1.rdf
			outputPath = arquivoAlinhamentos + "Alinhamento.owl";
			StorageManager.saveSemanticAlignmentsToRDF(aligns, outputPath);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return outputPath;
	}
		
	public static void main(String[] args) {
		System.out.println(new Wrapper().getInstance().benchmark("peer1"));
	}
}
